/*
 * InputStream.java
 * 
 * This file is part of Netuno Psamata.
 * 
 * CajuScript is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3, or (at your option) 
 * any later version.
 * 
 * CajuScript is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with CajuScript.  If not, see <http://www.gnu.org/licenses/>.
*/

package org.netuno.psamata.io;

import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;
import java.io.PushbackInputStream;
import java.io.StringWriter;
import java.nio.ByteBuffer;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.Channels;
import java.nio.channels.WritableByteChannel;

import org.apache.commons.io.IOUtils;

/**
 * Input Stream.
 * @author eduveks
 */
public class InputStream extends java.io.InputStream {
    private java.io.InputStream in = null;
    /**
     * Input Stream.
     * @param in InputStream
     */
    public InputStream(final java.io.InputStream in) {
        this.in = in;
    }
    /**
     * Read all.
     * @return Content
     * @throws IOException Reader Exception
     */
    public final String readAll() throws IOException {
        ReadableByteChannel channelIn = Channels.newChannel(this); 
        ByteBuffer buf = ByteBuffer.allocate(128);
        StringBuilder result = new StringBuilder();
        int len = buf.limit();
        while (len == buf.limit()) {
            len = channelIn.read(buf);
            byte[] bytes = new byte[len];
            System.arraycopy(buf.array(), 0, bytes, 0, len);
            result.append(new String(bytes));
            buf.clear();
        }
        return result.toString();
    }
    /**
     * Read all bytes.
     * @return Content
     * @throws IOException Reader Exception
     */
    public static byte[] readAllBytes(final java.io.InputStream in) throws IOException {
        ReadableByteChannel channelIn = Channels.newChannel(in); 
        ByteBuffer buf = ByteBuffer.allocate(in.available());
        channelIn.read(buf);
        return buf.array();
    }
    /**
     * Read all bytes from file.
     * @return Content
     * @throws IOException Reader Exception
     */
    public static byte[] readAllBytesFromFile(final String file) throws IOException {
    	return readAllBytesFromFile(new java.io.File(file));
    }
    /**
     * Read all bytes from file.
     * @return Content
     * @throws IOException Reader Exception
     */
    public static byte[] readAllBytesFromFile(final java.io.File file) throws IOException {
    	ByteBuffer buf = ByteBuffer.allocate((int)file.length());
        ReadableByteChannel channelIn = Channels.newChannel(new java.io.FileInputStream(file));
        try {
        	channelIn.read(buf);
        } finally {
        	channelIn.close();
        }
        return buf.array();
    }
    /**
     * Read all.
     * @param in Input Stream
     * @return Content
     * @throws IOException Reader Exception
     */
    public static String readAll(final java.io.InputStream in)
    throws IOException {
        ReadableByteChannel channelIn = Channels.newChannel(in);
        ByteBuffer buf = ByteBuffer.allocate(1024);
        StringBuilder result = new StringBuilder();
        int len = buf.limit();
        while (len == buf.limit()) {
            len = channelIn.read(buf);
            if (len < 0) {
                buf.clear();
                break;
            }
            byte[] bufBytes = buf.array();
            if (bufBytes.length != len) {
                byte[] bytes = new byte[len];
                System.arraycopy(bufBytes, 0, bytes, 0, len);
                result.append(new String(bytes));
            } else {
                result.append(new String(bufBytes));
            }
            buf.clear();
        }
        return result.toString();
    }
    /**
     * Read all.
     * @param r Buffered Reader
     * @return Content
     * @throws IOException Reader Exception
     */
    public static String readAll(final Reader r) throws IOException {
        StringBuffer result = new StringBuffer();
        char[] buf = new char[1024];
        int len = buf.length;
        while (len <= 0 || len == buf.length) {
            len = r.read(buf);
            if (len == -1) {
                break;
            }
            char[] chars = new char[len];
            System.arraycopy(buf, 0, chars, 0, len);
            result.append(new String(chars));
        }
        return result.toString();
    }
    /**
     * Read line.
     * @param in Input Stream
     * @return Line
     * @throws java.io.IOException Exception
     */
    public static String readLine(java.io.InputStream in)
    throws IOException {
        StringBuffer line = new StringBuffer();
        int c = -1;
        while ((c = in.read()) > -1) {
            if (c == '\n') {
                break;
            } else if (c == '\r') {
                int c2 = in.read();
                if ((c2 != '\n') && (c2 != -1)) {
                    if (!(in instanceof PushbackInputStream)) {
                        in = new PushbackInputStream(in);
                    }
                    ((PushbackInputStream) in).unread(c2);
                }
                break;
            }
            line.append((char)c);
        }
        if (c == -1) {
            return !line.toString().equals("") ? line.toString() : null;
        }
        return line.toString();
    }
    /**
     * Read buffer.
     * @param in Input Stream
     * @param bufferSize Size of the buffer to be allocated
     * @return Content
     * @throws IOException Reader Exception
     */
    public static byte[] readBufferBytes(final java.io.InputStream in, final int bufferSize) throws IOException {
        ReadableByteChannel channelIn = Channels.newChannel(in);
        ByteBuffer buf = ByteBuffer.allocate(bufferSize);
        channelIn.read(buf);
        return buf.array();
    }
    /**
     * Read buffer.
     * @param in Input Stream
     * @param bufferSize Size of the buffer to be allocated
     * @return Content
     * @throws IOException Reader Exception
     */
    public static String readBuffer(final java.io.InputStream in, final int bufferSize)
    throws IOException {
        return new String(readBufferBytes(in, bufferSize));
    }
    /**
     * Read buffer.
     * @param r Buffered Reader
     * @param bufferSize Size of the buffer to be allocated
     * @return Content
     * @throws IOException Reader Exception
     */
    public static String readBuffer(final Reader r, final int bufferSize) throws IOException {
        char[] buf = new char[bufferSize];
        int len = r.read(buf);
        if (len == -1) {
            return "";
        }
        char[] chars = new char[len];
        System.arraycopy(buf, 0, chars, 0, len);
        return new String(chars);
    }
    
    public static String readFromFile(java.io.File path) throws FileNotFoundException, IOException {
        Reader inputFile = null;
        try {
        	inputFile = new FileReader(path);
        	return InputStream.readAll(inputFile);
        } finally {
            if (inputFile != null) {
            	inputFile.close();
            }
        }
    }

    public static String readFromFile(String path) throws FileNotFoundException, IOException {
        return readFromFile(new java.io.File(path));
    }
    
    /**
     * Write content from input to some output.
     * @param out Output to write content from input
     * @throws IOException Exception
     */
    public void writeTo(OutputStream out) throws IOException {
    	ReadableByteChannel channelIn = Channels.newChannel(in);
    	WritableByteChannel channelOut = Channels.newChannel(out);
        ByteBuffer buf = ByteBuffer.allocate(in.available());
        channelIn.read(buf);
        channelOut.write(buf);
    }
    
    /**
     * Close.
     * @throws IOException Close exception
     */
    @Override
    public final void close() throws IOException {
        in.close();
    }
    /**
     * Read.
     * @return Byte
     * @throws java.io.IOException Read exception
     */
    @Override
    public final int read() throws IOException {
        return in.read();
    }
    
    public final String readString() throws IOException {
    	StringWriter writer = new StringWriter();
		IOUtils.copy(this, writer);
		return writer.toString();
    }
}
